#include "kdcmplayermencoderjob.h"

#include <QByteArray>

#include <KLocale>

KDCMPlayerMencoderJob::KDCMPlayerMencoderJob( KDCMPlayerMencoderJob::Mode mode, QObject *parent )
	: KDCAbstractKProcessJob( parent ), p_mode( mode ), p_curSec( 0 ), p_id( 0 ), p_timerID( -1 )
{

}

KDCMPlayerMencoderJob::~KDCMPlayerMencoderJob()
{

}

void KDCMPlayerMencoderJob::start()
{
	if ( isRunning() )
		return;

	QStringList arg;
	//arg << "-n" << QString::number( KDCConfig::niceLevel() );
	if ( p_mode == MPlayer )
	{
		arg << "mplayer";
		addArgument( "-slave" );
	}
	else
	{
		arg << "mencoder";
		process()->disconnect( SIGNAL( readyReadStandardOutput() ) );
		connect( process(), SIGNAL( readyReadStandardOutput() ), this, SLOT( readyReadMencoderProcessOutput() ) );
	}

	emit jobProgram( program() );
	if ( !startupMessage().isEmpty() )
		emit infoMessage( this, startupMessage() );

	p_id = 1;
	setFlag( 3, FALSE );
	p_info.vID = -1;
	p_info.aID = -1;
	p_info.aBitrate = -1;
	p_info.width = -1;

	process()->setProgram( "nice", arg + arguments() + p_files );
	process()->start();

	p_timerID = startTimer( 1000 );
}

void KDCMPlayerMencoderJob::write( const QString& str )
{
	if ( isRunning() )
		process()->write( str.toUtf8() );
}

bool KDCMPlayerMencoderJob::isRunning()
{
	if ( process()->state() == QProcess::Running )
		return TRUE;
	else
		return FALSE;
}

void KDCMPlayerMencoderJob::setFiles( const QStringList& files )
{
	p_files = files;
}

void KDCMPlayerMencoderJob::setFile( const QString& file )
{
	p_files.clear();
	p_files << file;
}

void KDCMPlayerMencoderJob::processFinished( int code, QProcess::ExitStatus status )
{
	killTimer( p_timerID );
	if ( ( status == QProcess::CrashExit || code != 0 ) && !flag( 3 ) )
		setStandardErrorMessage( code );
	else
		if ( !flag( 3 ) )
		{
			if ( !endingMessage().isEmpty() )
				emit infoMessage( this, endingMessage() );
			setPercent( 100 );
		}
	resetFlags();
	emitResult();
}

void KDCMPlayerMencoderJob::timerEvent( QTimerEvent *event )
{
	if ( isRunning() && p_mode == MPlayer && !flag( 1 ) && !flag( 2 ) )
		write( "get_time_pos\n" );
}

void KDCMPlayerMencoderJob::readyReadProcessOutput()
{
	QByteArray data( process()->readAllStandardOutput() );
	if ( data.startsWith( "ID_" ) )
		readyReadMPlayerInfoProcessOutput( data );
	else
	{
		if( data.startsWith( "ANS_TIME" ) )
		{
			QByteArray temp;
			int idx = 18;
			char byte = data.at( idx );
			while ( byte != '\n' )
			{
				temp.append( byte );
				++idx;
				byte = data.at( idx );
			}
			p_curSec = qRound( temp.toDouble() - 1.0 );
			emit timeChanged( p_curSec );
		}
		else
		{
			int idx = data.indexOf( "[CROP]" );
			if ( idx != -1 )
			{
				idx = data.indexOf( '=', idx + 49 ) + 1;
				int width = extract( data, idx, ':' ).toInt();

				idx++;
				int height = extract( data, idx, ':' ).toInt();

				idx++;
				int cropX = extract( data, idx, ':' ).toInt();

				idx++;
				int cropY = extract( data, idx, ')' ).toInt();
				emit cropped( width, height, cropX, cropY );
			}
			else
			{
				if ( data.indexOf( "SUB: Could not determine file format" ) != -1 )
				{
					setFlag( 3, TRUE );
					setError( -1 );
					setErrorText( i18n("Could not determine subtitles file format") );
					process()->close();
				}
				else
					if ( data.indexOf( "Cannot load subtitles:" ) != -1 )
					{
						setFlag( 3, TRUE );
						setError( -1 );
						setErrorText( i18n("Cannot load subtitles file. Perhaps it's not subtitle file.") );
						process()->close();
					}
				else
				{
					idx = data.indexOf( "FATAL" );
					if ( idx != -1 )
					{
						setFlag( 3, TRUE );
						setError( -1 );
						setErrorText( QString( data.mid( idx ) ) );
						process()->close();
					}
				}
			}
		}
	}
}

void KDCMPlayerMencoderJob::readyReadMencoderProcessOutput()
{
	QByteArray data( process()->readAllStandardOutput() );
	if ( data.lastIndexOf( "Pos", 3 ) != -1 )
	{
		int idx = data.indexOf( '.', 7 ) - 1;
		char byte = data.at( idx );
		QByteArray temp;
		while ( byte != ' ' )
		{
			temp.prepend( byte );
			--idx;
			byte = data.at( idx );
		}
		setProcessedAmount( KJob::Bytes, temp.toInt() );
		temp.clear();
		idx = data.indexOf( '%', 22 ) + 2; //tmp + 5
		emitSpeed( qRound( extract( data, idx, 'f' ).toDouble() ) );
	}
	else
	{
		int idx = data.indexOf( "FATAL" );
		if ( idx != -1 )
		{
			setFlag( 3, TRUE );
			setError( -1 );
			setErrorText( QString( data.mid( idx ) ) );
			process()->close();
		}
	}
}

const KDCMPlayerFileInfo KDCMPlayerMencoderJob::info() const
{
	return p_info;
}

void KDCMPlayerMencoderJob::readyReadMPlayerInfoProcessOutput( QByteArray& data )
{
	if ( data.startsWith( "ID_VIDEO_I" ) )
		p_info.vID = extract( data, 12 ).toInt();
	else
		if ( data.startsWith( "ID_AUDIO_I" ) )
		{
			p_info.aID = extract( data, 12 ).toInt();
			emit foundAudioStream( p_info.aID );
		}
	else
		if ( data.startsWith( "ID_D" ) )
			p_info.demuxer = QString( extract( data, 11 ) );
	else
		if ( data.startsWith( "ID_VIDEO_FO" ) )
			p_info.vFormat = QString( extract( data, 16 ) );
	else
		if ( data.startsWith( "ID_VIDEO_B" ) )
			p_info.vBitrate = extract( data, 17 ).toInt()/1000.0;
	else
		if ( data.startsWith( "ID_VIDEO_FP" ) )
			p_info.fps = extract( data, 13 ).toDouble();
	else
		if ( data.startsWith( "ID_VIDEO_H" ) )
			p_info.height = extract( data, 16 ).toInt();
	else
		if ( data.startsWith( "ID_VIDEO_W" ) )
			p_info.width = extract( data, 15 ).toInt();
	else
		if ( data.startsWith( "ID_VIDEO_A" ) )
			p_info.ar = extract( data, 16 ).toDouble();
	else
		if ( data.startsWith( "ID_L" ) )
			p_info.length = qRound( extract( data, 10 ).toDouble() );
	else
		if ( data.startsWith( "ID_AUDIO_F" ) )
			p_info.aFormat = QString( extract( data, 16 ) );
	else
		if ( data.startsWith( "ID_AUDIO_B" ) )
		{
			p_info.aBitrate = extract( data, 17 ).toInt()/1000.0;
			int idx = data.indexOf( "_R", 17 );
			if ( idx != -1 )
			{
				idx += 6;
				p_info.srate = extract( data, idx ).toInt();
				idx = data.indexOf( "=", idx + 2 ) + 1;
				p_info.nch = extract( data, idx ).toInt();
			}
		}
}

void KDCMPlayerMencoderJob::pause()
{
	if ( isRunning() && !flag( 1 ) && !flag( 2 ) )
	{
		write( "pause\n" );
		setFlag( 1, TRUE );
	}
}

void KDCMPlayerMencoderJob::restart()
{
	if ( !isRunning() )
		return;

	process()->blockSignals( TRUE );
	int id = currentMovie();
	int pos = currentTime();
	killTimer( p_timerID );

	write( "quit\n" );
	if ( !process()->waitForFinished() )
		process()->close();
	start();
	setCurrentMovie( id );
	process()->waitForStarted();
	if ( id > 1 )
		playNext( id - 1 );
	write( QString( "seek " + QString::number( pos ) + " 2\n" ) );
	if ( flag( 2 ) )
		write( "pause\n" );
	process()->blockSignals( FALSE );
}

void KDCMPlayerMencoderJob::unpause()
{
	if ( isRunning() && flag( 1 ) && !flag( 2 ) )
	{
		write( "pause\n" );
		setFlag( 1, FALSE );
	}
}

void KDCMPlayerMencoderJob::forcePause()
{
	if ( isRunning() )
	{
		if ( !flag( 2 ) ) // we need pause
		{
			pause();
			setFlag( 2, TRUE );
		}
		else // we need unpause
		{
			setFlag( 2, FALSE );
			unpause();
		}
	}
}

void KDCMPlayerMencoderJob::stop()
{
	if( !isRunning() )
		return;

	setFlag( 1, FALSE );
	setFlag( 2, FALSE );
	process()->write( "quit\n" );
	if ( !process()->waitForFinished() )
		process()->close();
}

void KDCMPlayerMencoderJob::setSeek( int value )
{
	if ( isRunning() && !flag( 1 ) && !flag( 2 ) )
	{
		write( QString( "seek " + QString::number( value ) + " 2\n" ) );
		setFlag( 1, FALSE );
		setFlag( 2, FALSE );
		p_curSec = value;
		emit timeChanged( value );
	}
}

void KDCMPlayerMencoderJob::playNext( int id )
{
	if ( !isRunning() )
		return;

	write( QString( "pt_step %1\n" ).arg( qAbs( id ) ) );

	if ( flag( 2 ) )
		write( "pause\n" );
}

void KDCMPlayerMencoderJob::playPrev( int id )
{
	if ( !isRunning() )
		return;

	write( QString( "pt_step -%1\n" ).arg( qAbs( id ) ) );

	if ( flag( 2 ) )
		write( "pause\n" );
}

void KDCMPlayerMencoderJob::changeAudioID( int id )
{
	int idx = arguments().indexOf( "-aid" );
	if ( idx != -1 )
	{
		int tmp = arguments().at( idx + 1 ).toInt();
		addArgument( "-aid", QString::number( id ) );
		if ( tmp != id && isRunning() )
			restart();
	}
}

void KDCMPlayerMencoderJob::changeVideoDriver( const QString& driver )
{
	int idx = arguments().indexOf( "-vo" );
	if ( idx != -1 )
	{
		QString tmp = arguments().at( idx + 1 );
		addArgument( "-vo", driver );
		if ( tmp != driver && isRunning() )
			restart();
	}
}

int KDCMPlayerMencoderJob::currentTime() const
{
	return p_curSec;
}

bool KDCMPlayerMencoderJob::isPause()
{
	return flag( 2 );
}

int KDCMPlayerMencoderJob::currentMovie() const
{
	return p_id;
}

void KDCMPlayerMencoderJob::setCurrentMovie( int id )
{
	p_id = id;
}
